<template>
  <div v-if="!stateLoading" class="flex flex-col h-full" :class="{'pb-4': windowScrollerSeen && selectedRows.length === 0}">
    <div
      class="relative flex flex-col flex-grow z-10"
      :class="{'overflow-y-hidden pb-20': currentViewRender === 'cards'}"
    >
      <Toolbar
        :toolbarActions="toolbarActions"
        @toolbar:action="emitAction"
        :searchValue="searchValue"
        @search:change="setSearchValue"
        :activePageUrl="activePageUrl"
        :archivedPageUrl="archivedPageUrl"
        :currentViewMode="currentViewMode"
        :currentViewRender="currentViewRender"
        :viewRenders="viewRenders"
        :filters="filters"
        :columnDefs="columnDefs"
        :tableState="tableState"
        :order="order"
        @applyFilters="applyFilters"
        @setTableView="switchViewRender('table')"
        @setCardsView="switchViewRender('cards')"
        @sort="applyOrder"
        @hideColumn="hideColumn"
        @showColumn="showColumn"
        @pinColumn="pinColumn"
        @unPinColumn="unPinColumn"
        @reorderColumns="reorderColumns"
        @resetColumnsToDefault="resetColumnsToDefault"
      />
      <div v-if="this.objectArchived && this.currentViewMode === 'active'" class="pt-6" >
        <em> {{ hiddenDataMessage }} </em>
      </div>
      <div v-else class="w-full h-full" :class="{ '!h-[calc(100%-68px)]': selectedRows.length > 0 && actionsUrl }">
        <div v-if="currentViewRender === 'cards'" ref="cardsContainer" @scroll="handleScroll"
            class="flex-grow basis-64 overflow-y-auto h-full overflow-x-visible p-2 -ml-2">
          <div class="grid gap-4" :class="gridColsClass">
            <slot v-for="element in rowData" :key="element.id" name="card" :dtComponent="this" :params="element"></slot>
          </div>
        </div>
        <ag-grid-vue
          v-if="currentViewRender === 'table'"
          class="ag-theme-alpine w-full flex-grow h-full z-10"
          :class="{'opacity-0': initializing }"
          :columnDefs="extendedColumnDefs"
          :rowData="rowData"
          :defaultColDef="defaultColDef"
          :rowSelection="'multiple'"
          :suppressRowTransform="true"
          :gridOptions="gridOptions"
          :suppressRowClickSelection="true"
          :getRowClass="getRowClass"
          :enableCellTextSelection="true"
          @grid-ready="onGridReady"
          @first-data-rendered="onFirstDataRendered"
          @sortChanged="setOrder"
          @columnResized="onColumnResized"
          @columnMoved="onColumnMoved"
          @bodyScroll="handleScroll"
          @columnPinned="handlePin"
          @columnVisible="handleVisibility"
          @rowSelected="setSelectedRows"
          @cellClicked="clickCell"
          :CheckboxSelectionCallback="withCheckboxes"
        >
        </ag-grid-vue>
        <div v-if="dataLoading" class="flex absolute top-0 items-center justify-center w-full flex-grow h-full z-10">
          <img src="/images/medium/loading.svg" alt="Loading" class="p-16 rounded-xl bg-sn-white" />
        </div>
      </div>
      <ActionToolbar
        v-if="selectedRows.length > 0 && actionsUrl"
        :actionsUrl="actionsUrl"
        :actionsMethod="actionsMethod"
        :params="actionsParams"
        @toolbar:action="emitAction" />
    </div>
    <div v-if="scrollMode == 'pages'" class="flex items-center py-4" :class="{'opacity-0': initializing }" data-e2e="e2e-CO-tableInfo">
      <div class="flex items-center gap-4" data-e2e="e2e-TX-tableInfo-show">
        {{ i18n.t('datatable.show') }}
        <div class="w-36">
          <SelectDropdown
            :value="perPage"
            :options="perPageOptions"
            :data-e2e="'e2e-DD-tableInfo-rows'"
            @change="setPerPage"
          ></SelectDropdown>
        </div>
        <div v-show="!dataLoading" data-e2e="e2e-TX-tableInfo-entries">
          <span v-if="selectedRows.length">
            {{ i18n.t('datatable.entries.selected', { count: totalEntries, selected: selectedRows.length }) }}
          </span>
          <span v-else>
            {{ i18n.t('datatable.entries.total', { count: totalEntries, selected: selectedRows.length }) }}
          </span>
        </div>
      </div>
      <div class="ml-auto">
        <Pagination
          :totalPage="totalPage"
          :currentPage="page"
          @setPage="setPage"
        ></Pagination>
      </div>
    </div>
  </div>
</template>

<script>

/* global GLOBAL_CONSTANTS */
import { AgGridVue } from 'ag-grid-vue3';
import PerfectScrollbar from 'vue3-perfect-scrollbar';
import axios from '../../../packs/custom_axios.js';
import SelectDropdown from '../select_dropdown.vue';
import Pagination from './pagination.vue';
import CustomHeader from './tableHeader';
import ActionToolbar from './action_toolbar.vue';
import Toolbar from './toolbar.vue';
import RowMenuRenderer from './row_menu_renderer.vue';

export default {
  name: 'App',
  props: {
    withCheckboxes: {
      type: Boolean,
      default: true
    },
    withRowMenu: {
      type: Boolean,
      default: false
    },
    tableId: {
      type: String,
      required: true
    },
    columnDefs: {
      type: Array,
      default: () => []
    },
    dataUrl: {
      type: String,
      required: true
    },
    actionsUrl: {
      type: String
    },
    actionsMethod: {
      type: String
    },
    toolbarActions: {
      type: Object,
      required: true
    },
    reloadingTable: {
      type: Boolean,
      default: false
    },
    activePageUrl: {
      type: String
    },
    archivedPageUrl: {
      type: String
    },
    currentViewMode: {
      type: String,
      default: 'active'
    },
    viewRenders: {
      type: Object
    },
    filters: {
      type: Array,
      default: () => []
    },
    scrollMode: {
      type: String,
      default: 'pages'
    },
    objectArchived: {
      type: Boolean,
      default: false
    },
    hiddenDataMessage: {
      type: String
    }
  },
  data() {
    return {
      rowData: [],
      gridApi: null,
      columnApi: null,
      defaultColDef: {
        resizable: true
      },
      perPage: 20,
      page: 1,
      order: null,
      totalPage: 0,
      totalEntries: null,
      selectedRows: [],
      keepSelection: false,
      searchValue: '',
      initializing: true,
      activeFilters: {},
      currentViewRender: 'table',
      cardCheckboxes: [],
      dataLoading: true,
      stateLoading: true,
      lastPage: false,
      tableState: null,
      userSettingsUrl: null,
      gridReady: false,
      windowScrollerSeen: false,
      resetGridCols: false,
      navigatorCollapsed: false,
      gridColsClass: ''
    };
  },
  components: {
    AgGridVue,
    SelectDropdown,
    PerfectScrollbar,
    Pagination,
    agColumnHeader: CustomHeader,
    ActionToolbar,
    Toolbar,
    RowMenuRenderer
  },
  computed: {
    perPageOptions() {
      return [10, 20, 50, 100].map((value) => ([value, `${value} ${this.i18n.t('datatable.rows')}`]));
    },
    actionsParams() {
      return {
        items: JSON.stringify(this.selectedRows.map((row) => ({ id: row.id, type: row.type })))
      };
    },
    gridOptions() {
      return {
        suppressCellFocus: true,
        rowHeight: 40,
        headerHeight: 40,
        getRowId: (params) => `e2e-TB-row-${params.data.code || params.data.id}`
      };
    },
    extendedColumnDefs() {
      const columns = this.columnDefs.map((column) => ({
        ...column,
        minWidth: column.minWidth || 110,
        cellRendererParams: {
          dtComponent: this
        },
        pinned: (column.field === 'name' || column.field === 'name_hash' ? 'left' : null),
        comparator: () => null
      }));

      if (this.withCheckboxes) {
        columns.unshift({
          field: 'checkbox',
          headerCheckboxSelection: true,
          headerCheckboxSelectionFilteredOnly: true,
          checkboxSelection: true,
          suppressMovable: true,
          width: 40,
          minWidth: 40,
          maxWidth: 40,
          resizable: true,
          pinned: 'left',
          lockPosition: 'left'
        });
      }

      if (this.withRowMenu) {
        columns.push({
          field: 'rowMenu',
          headerName: '',
          width: 42,
          minWidth: 42,
          resizable: false,
          sortable: false,
          suppressMovable: true,
          cellRenderer: 'RowMenuRenderer',
          cellRendererParams: {
            dtComponent: this
          },
          cellStyle: {
            padding: 0,
            display: 'flex',
            justifyContent: 'center',
            alignItems: 'center',
            overflow: 'visible'
          }
        });
      }

      return columns;
    },
    stateKey() {
      return `${this.tableId}_${this.currentViewMode}_state`;
    }
  },
  watch: {
    reloadingTable() {
      if (this.reloadingTable) {
        this.updateTable();
        this.$nextTick(() => {
          this.selectedRows = [];
          this.gridApi?.deselectAll();
        });
      }
    },
    perPage() {
      this.saveTableState();
    },
    resetGridCols() {
      if (this.resetGridCols) {
        this.setGridColsClass();
        this.resetGridCols = false;
      }
    },
    currentViewRender() {
      if (this.currentViewRender === 'cards') {
        this.setGridColsClass();
      }
    }
  },
  created() {
    window.resetGridColumns = (newNavigatorCollapsed) => {
      setTimeout(() => {
        this.navigatorCollapsed = newNavigatorCollapsed;
        this.setGridColsClass();
      }, 400);
    };
    this.userSettingsUrl = document.querySelector('meta[name="user-settings-url"]').getAttribute('content');
    this.fetchTableState();
  },
  mounted() {
    this.navigatorCollapsed = document.querySelector('.sci--layout').getAttribute('data-navigator-collapsed') === 'true';
    this.setGridColsClass();

    window.addEventListener('resize', this.resize);
  },
  beforeDestroy() {
    delete window.resetGridColumns;
    window.removeEventListener('resize', this.resize);
  },
  methods: {
    setGridColsClass() {
      if (this.currentViewRender !== 'cards') return;
      const availableGridWidth = document.querySelector('.sci--layout-content').offsetWidth;
      const { paddingLeft, paddingRight } = getComputedStyle(document.querySelector('.sci--layout-content'));
      const padding = parseInt(paddingLeft, 10) + parseInt(paddingRight, 10);

      let maxGridCols = Math.floor((availableGridWidth - padding) / GLOBAL_CONSTANTS.TABLE_CARD_MIN_WIDTH) || 1;
      if (maxGridCols > 1) {
        const gap = (maxGridCols - 1) * GLOBAL_CONSTANTS.TABLE_CARD_GAP;
        maxGridCols = Math.floor((availableGridWidth - gap - padding) / GLOBAL_CONSTANTS.TABLE_CARD_MIN_WIDTH);
      }
      // grid-cols-2 grid-cols-3 grid-cols-4 grid-cols-5 grid-cols-6 grid-cols-7 grid-cols-8 grid-cols-9 grid-cols-10
      if (maxGridCols > 10) maxGridCols = 10;

      if (this.navigatorCollapsed) {
        this.gridColsClass = 'grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4';
      } else {
        this.gridColsClass = `grid-cols-${maxGridCols}`;
      }
    },
    handleScroll() {
      if (this.scrollMode === 'pages' || this.scrollMode === 'none') return;

      let target = null;
      if (this.currentViewRender === 'cards') {
        target = this.$refs.cardsContainer;
      } else {
        target = document.querySelector('.ag-body-viewport');
      }

      if (!target) return;

      if (target.scrollTop + target.clientHeight >= target.scrollHeight - 50) {
        if (this.dataLoading || this.lastPage) return;

        this.dataLoading = true;
        this.page += 1;
        this.loadData();
      }
    },
    handlePin(event) {
      if (event.pinned === 'right') {
        this.columnApi.setColumnPinned(event.column.colId, null);
      }
      this.saveTableState();
    },
    handleVisibility(event) {
      if (!event.visible && event.source !== 'api') {
        this.columnApi.setColumnVisible(event.column.colId, true);
      }
      this.saveTableState();
    },
    // Table states
    fetchTableState() {
      axios.get(this.userSettingsUrl, { params: { key: this.stateKey } })
        .then((response) => {
          if (response.data.data) {
            this.tableState = response.data.data;
            this.currentViewRender = this.tableState.currentViewRender;
            this.perPage = this.tableState.perPage;
            this.order = this.tableState.order;
            if (this.currentViewRender === 'cards') {
              this.initializing = false;
            }
          }
          this.stateLoading = false;
          this.loadData();
        });
    },
    applyTableState() {
      const { columnsState } = this.tableState;
      this.columnsState = columnsState;

      if (this.order) {
        this.tableState.columnsState.forEach((column) => {
          const updatedColumn = column;
          updatedColumn.sort = this.order.column === column.colId ? this.order.dir : null;
          return updatedColumn;
        });
      }
      this.columnApi.applyColumnState({
        state: this.tableState.columnsState,
        applyOrder: true
      });

      setTimeout(() => {
        this.initializing = false;
      }, 200);
    },
    saveTableState() {
      if (this.initializing) {
        return;
      }
      const columnsState = this.columnApi ? this.columnApi.getColumnState() : this.tableState?.columnsState || [];
      const tableState = {
        columnsState,
        order: this.order,
        currentViewRender: this.currentViewRender,
        perPage: this.perPage
      };
      const settings = {
        key: this.stateKey,
        data: tableState
      };
      axios.put(this.userSettingsUrl, { settings: [settings] });
      this.tableState = tableState;
    },
    getRowClass() {
      if (this.currentViewMode === 'archived') {
        return '!bg-sn-super-light-grey';
      }
      return '';
    },
    formatData(data) {
      return data.map((item) => ({
        ...item.attributes,
        id: item.id,
        type: item.type
      }));
    },
    resize() {
      this.windowScrollerSeen = document.documentElement.scrollWidth > document.documentElement.clientWidth;
      this.setGridColsClass();
      if (this.tableState) return;

      this.columnApi?.autoSizeAllColumns();
    },
    updateTable() {
      if (this.scrollMode === 'pages') {
        this.loadData();
      } else {
        this.reloadTable();
      }
    },
    reloadTable(clearSelection = true) {
      if (this.dataLoading) return;

      this.dataLoading = true;
      if (clearSelection) {
        this.selectedRows = [];
        this.gridApi?.deselectAll();
      }
      this.page = 1;
      this.loadData(true);
    },
    loadData(reload = false) {
      axios
        .get(this.dataUrl, {
          params: {
            per_page: this.perPage,
            page: this.page,
            order: this.order,
            search: this.searchValue,
            view_mode: this.currentViewMode,
            filters: this.activeFilters
          }
        })
        .then((response) => {
          if (reload) {
            if (this.gridApi) this.gridApi.setRowData([]);
            this.rowData = [];
          }

          if (this.scrollMode === 'pages' || this.scrollMode === 'none') {
            if (this.gridApi) this.gridApi.setRowData(this.formatData(response.data.data));
            this.rowData = this.formatData(response.data.data);
          } else {
            this.handleInfiniteScroll(response);
          }

          if (this.scrollMode !== 'none') {
            this.totalPage = response.data.meta.total_pages;
            this.totalEntries = response.data.meta.total_count;
          }
          this.$emit('tableReloaded', this.rowData, { filtered: this.searchValue.length > 0 });
          this.dataLoading = false;
          this.restoreSelection();

          this.handleScroll();
        });
    },
    handleInfiniteScroll(response) {
      const newRows = this.rowData.slice();
      this.formatData(response.data.data).forEach((row) => {
        if (this.currentViewMode === 'active' && row.archived) {
          return;
        }
        newRows.push(row);
      });
      this.rowData = newRows;
      if (this.gridApi) {
        const viewport = document.querySelector('.ag-body-viewport');
        const { scrollTop } = viewport;
        this.gridApi.setRowData(this.rowData);
        this.$nextTick(() => {
          viewport.scrollTop = scrollTop;
        });
      }
      this.lastPage = !response.data.meta.next_page;
    },
    onGridReady(params) {
      this.gridApi = params.api;
      this.columnApi = params.columnApi;
      this.gridReady = true;
      if (this.tableState) {
        this.applyTableState();
      } else {
        this.gridApi.sizeColumnsToFit();
        this.initializing = false;
      }
      this.hideLastPinnedResizeCell();
    },
    onFirstDataRendered() {
      this.resize();
    },
    setPerPage(value) {
      this.perPage = value;
      this.page = 1;
      this.lastPage = false;
      this.reloadTable(false);
    },
    setPage(page) {
      this.page = page;
      this.loadData(false);
    },
    setOrder() {
      const orderState = this.getOrder(this.columnApi.getColumnState());
      const [order] = orderState;
      this.order = order;
      this.saveTableState();
      this.reloadTable(false);
    },
    restoreSelection() {
      if (this.gridApi) {
        this.gridApi.forEachNode((node) => {
          if (this.selectedRows.find((row) => row.id === node.data.id)) {
            node.setSelected(true);
          } else {
            node.setSelected(false);
          }
        });
        this.$emit('selectionChanged', this.selectedRows);
      }
    },
    setSelectedRows(e) {
      if (!this.rowData.find((row) => row.id === e.data.id)) return;

      if (e.node.isSelected()) {
        if (this.selectedRows.find((row) => row.id === e.data.id)) return;

        this.selectedRows.push(e.data);
      } else {
        this.selectedRows = this.selectedRows.filter((row) => row.id !== e.data.id);
      }
      this.$emit('selectionChanged', this.selectedRows);
    },
    emitAction(action) {
      this.$emit(action.name, action, this.selectedRows);
    },
    setSearchValue(value) {
      this.searchValue = value;
      this.reloadTable();
    },
    clickCell(e) {
      if (e.column.colId !== 'rowMenu' && e.column.userProvidedColDef.notSelectable !== true) {
        e.node.setSelected(true);
        this.$emit('selectionChanged', this.selectedRows);
      }
    },
    applyFilters(filters) {
      this.activeFilters = filters;
      this.reloadTable();
    },
    switchViewRender(view) {
      if (this.currentViewRender === view) return;
      this.currentViewRender = view;
      this.columnApi = null;
      this.gridApi?.deselectAll();
      this.gridApi = null;
      this.saveTableState();
      this.initializing = true;
      this.selectedRows = [];
    },
    hideColumn(column) {
      this.columnApi.setColumnVisible(column.field, false);
    },
    showColumn(column) {
      this.columnApi.setColumnVisible(column.field, true);
    },
    pinColumn(column) {
      this.columnApi.setColumnPinned(column.field, 'left');
      this.hideLastPinnedResizeCell();
    },
    unPinColumn(column) {
      this.columnApi.setColumnPinned(column.field, null);
      this.hideLastPinnedResizeCell();
    },
    reorderColumns(columns) {
      this.columnApi.moveColumns(columns, 1);
      this.saveTableState();
    },
    resetColumnsToDefault() {
      this.columnApi.resetColumnState();
      this.gridApi.sizeColumnsToFit();
      this.hideLastPinnedResizeCell();
    },
    getOrder(columnsState) {
      if (!columnsState) return null;

      return columnsState.filter((column) => column.sort)
        .map((column) => ({
          column: column.colId,
          dir: column.sort
        }));
    },
    applyOrder(column, dir) {
      this.order = {
        column,
        dir
      };
      this.saveTableState();
      this.reloadTable(false);
    },
    onColumnMoved(event) {
      if (event.finished) {
        this.hideLastPinnedResizeCell();
        this.saveTableState();
      }
    },
    onColumnResized(event) {
      if (event.finished) {
        this.saveTableState();
      }
    },
    hideLastPinnedResizeCell() {
      $('.ag-pinned-left-header .ag-header-cell .ag-header-cell-resize').css('opacity', 100);
      let lastPinnedColIndex = 0;
      // eslint-disable-next-line func-names
      $('.ag-pinned-left-header .ag-header-cell').each(function () {
        const colIndex = parseInt($(this).attr('aria-colindex'), 10);
        if (colIndex > lastPinnedColIndex) lastPinnedColIndex = colIndex;
      });
      $(`.ag-pinned-left-header .ag-header-cell[aria-colindex="${lastPinnedColIndex}"] .ag-header-cell-resize`).css('opacity', 0);
    }
  }
};
</script>
